#include "Arbol.h"
const int LENGTH_NOM = 30;

Arbol::Arbol() {
	this->nodoPadre = NULL;
	this->numeroNodos = 0;
	this->ultimoID = 0;
	this->nivelActual = 0;
	this->posicionPuntero=0;
	this->indDiscos[0] = 0;
	this->indDiscos[1] = 0;
	this->indDiscos[2] = 0;
	this->indDiscos[3] = 0;
}

Arbol::Arbol(Nodo* padre) {
	this->nodoPadre = padre;
	this->numeroNodos = 0;
	this->ultimoID = 0;
	this->nivelActual = 0;
	this->posicionPuntero=0;
	this->indDiscos[0] = 0;
	this->indDiscos[1] = 0;
	this->indDiscos[2] = 0;
	this->indDiscos[3] = 0;
}

Arbol::~Arbol() {
	this->LiberarArbol(this->nodoPadre);
}

void Arbol::LiberarArbol(Nodo* padre) {
	if (padre->ChildNum() == 0) {
		padre->~Nodo();
	} else {
		int i;
		for (i=0; i<padre->ChildNum(); i++) {
			LiberarArbol(padre->GetHijo(i));
		}
	}
}

void Arbol::CambiarNumNodos(int cambio) {
	this->numeroNodos += cambio;
}

void Arbol::SetDirActual(Nodo* padre) {
	this->directorioActual = padre;
}

void Arbol::GuardarArbol(Nodo* padre, ofstream* f) {
	string nom = padre->GetNombre();
	int controlNumHijos = padre->ChildNum();
	const char* buff = new char [LENGTH_NOM];
	buff = nom.c_str();
	bool esDir = padre->GetEsDir();
	off_t tamano = padre->GetTamano();
	time_t time = padre->GetUltMod();
	int childNum = padre->ChildNum();
	(*f).write((char*)&controlNumHijos,sizeof(int));
	(*f).write((char*)buff,sizeof(char)*LENGTH_NOM);
	(*f).write((char*)&esDir,sizeof(bool));
	(*f).write((char*)&tamano,sizeof(off_t));
	(*f).write((char*)&time,sizeof(time_t));
	(*f).write((char*)&childNum,sizeof(int));
	int numBl = padre->GetNumBloques();
	(*f).write((char*)&numBl,sizeof(int));
	int i, bl;
	for (i=0; i<padre->GetNumBloques(); i++) {
		bl = padre->GetBloqueIndiceByIndex(i);
		(*f).write((char*)&bl,sizeof(int));
	}
	if (!padre->ChildNum() == 0) {
		int i;
		for(i=0;i<padre->ChildNum();i++)
			GuardarArbol(padre->GetHijo(i),f);
	}
}

void Arbol::CargarArbol(Nodo* padre, ifstream* f, int* id) {
	int controlNumHijos = 0;
	(*f).read((char*)&controlNumHijos,sizeof(int));
	if (padre->GetNumHijos() != 0) {
		int i;
		for(i=0;i<padre->GetNumHijos();i++) {
			string nom = "";
			const char* buff = new char [LENGTH_NOM];
			bool esDir = true;
			off_t tamano = 0;
			time_t time = 0;
			int childNum = 0;
			(*f).read((char*)buff,sizeof(char)*LENGTH_NOM);
			nom.assign(buff);
			(*f).read((char*)&esDir,sizeof(bool));
			(*f).read((char*)&tamano,sizeof(off_t));
			(*f).read((char*)&time,sizeof(time_t));
			(*f).read((char*)&childNum,sizeof(int));
			int numBl = 0;
			(*f).read((char*)&numBl,sizeof(int));
			int i, bl;
			Nodo* hijo = new Nodo(nom,*id,0,true);
			for (i=0; i<numBl; i++) {
				(*f).read((char*)&bl,sizeof(int));
				hijo->AnadirBloqueIndice(bl);
			}
			hijo->SetEsDir(esDir);
			hijo->SetTamano(tamano);
			hijo->SetTimeUltId(time);
			hijo->SetNumHijos(childNum);
			int numHijos = padre->GetNumHijos();
			padre->SetNumHijos(numHijos-1);
			(*id)++;
			padre->AnadirHijo(hijo);
			CargarArbol(hijo,f,id);
		}
	}
}

void Arbol::LoadTree() {
	int* id = new int;
	*id = 0;
	ifstream farbol("arbol.dat", ios::binary);
	if (farbol == NULL) {
		Nodo* padre = new Nodo("root",0,0,true);
		this->nodoPadre = padre;
		this->directorioActual = padre;
	} else {
		int i;
		for (i=0; i<4; i++) {
			farbol.read((char*)&this->indDiscos[i],sizeof(int));
		}
		farbol.read((char*)&this->posicionPuntero,sizeof(int));
		farbol.read((char*)&this->numeroNodos,sizeof(int));
		farbol.read((char*)&this->ultimoID,sizeof(int));
		int numHijos;
		farbol.read((char*)&numHijos,sizeof(int));
		int controlNumHijos = 0;
		farbol.read((char*)&controlNumHijos,sizeof(int));
		string nom = "";
		bool esDir = true;
		off_t tamano = 0;
		time_t time = 0;
		const char* buff = new char [LENGTH_NOM];
		int childNum = 0;
		farbol.read((char*)buff,sizeof(char)*LENGTH_NOM);
		nom.assign(buff);
		farbol.read((char*)&esDir,sizeof(bool));
		farbol.read((char*)&tamano,sizeof(off_t));
		farbol.read((char*)&time,sizeof(time_t));
		farbol.read((char*)&childNum,sizeof(int));
		int numBl = 0;
		farbol.read((char*)&numBl,sizeof(int));
		int bl;
		Nodo* padre = new Nodo("root",0,0,true);
		this->nodoPadre = padre;
		for (i=0; i<numBl; i++) {
			farbol.read((char*)&bl,sizeof(int));
			this->nodoPadre->AnadirBloqueIndice(bl);
		}
		this->directorioActual = this->nodoPadre;
		this->nodoPadre->SetNombre(nom);
		this->nodoPadre->SetNumHijos(numHijos);
		this->nodoPadre->SetTamano(tamano);
		this->nodoPadre->SetTimeUltId(time);
		CargarArbol(this->nodoPadre,&farbol,id);
		this->ultimoID = *id;
		farbol.close();
	}
	this->ReadFile();
}

void Arbol::SaveTree() {
	ofstream farbol("arbol.dat", ios::binary);
	if (farbol == NULL) {
		cerr << "Could not create arbol.dat!\n";
	} else {
		int numHijosPadre = this->nodoPadre->GetNumHijos();
		int i;
		for (i=0; i<4; i++) {
			farbol.write((char*)&this->indDiscos[i],sizeof(int));
		}
		farbol.write((char*)&this->posicionPuntero,sizeof(int));
		farbol.write((char*)&this->numeroNodos,sizeof(int));
		farbol.write((char*)&this->ultimoID,sizeof(int));
		farbol.write((char*)&numHijosPadre,sizeof(int));
		GuardarArbol(this->nodoPadre,&farbol);
		farbol.close();
	}
}

string Arbol::GetNomDir(){
	return this->directorioActual->GetNombre();
}

void Arbol::ls(){
	int i,numeroNodos;
	numeroNodos=this->directorioActual->ChildNum();
	if (numeroNodos == 0) {
		cout<<"El directorio actual está vacío."<<endl;
	}
	for(i=0;i<numeroNodos;i++){
		cout<<directorioActual->GetHijo(i)->GetNombre()<<" "<<endl;
	}
}

void Arbol::pwd(){
	string nom;
	Nodo* aux=this->directorioActual;
    bool control=false;
    vector<string> ruta;
	nom=aux->GetNombre();
	while(nom!="root"){
		nom=aux->GetNombre();
		aux=aux->GetPadre();
		ruta.insert(ruta.begin(),nom);
		control=true;
	}
	if (control){

		unsigned int i;
		for(i=0; i<ruta.size(); i++) {
			cout << ruta[i] << "/";
		}
		cout << endl;
	}
	 if (nom=="root" && !control)
		 cout<<nom<<"/"<<endl;
}

void Arbol::cd(string nomDir){
	if (this->directorioActual->GetNodoByNom(nomDir) != NULL || nomDir == "..") {
		if(nomDir==".."){
			if(this->directorioActual->GetNombre() != "root") {
				this->directorioActual=this->directorioActual->GetPadre();
				this->nivelActual--;
			} else {
				cout << "Ya está en el directorio más alto.\n";
			}
		}else{
			if (this->directorioActual->GetNodoByNom(nomDir)->GetEsDir()) {
				this->nivelActual++;
				int i=0;
				bool encontrado=false;
				while(i<this->directorioActual->ChildNum() && !encontrado){
					encontrado=this->directorioActual->GetHijo(i)->GetNombre()==nomDir;
					if(!encontrado){
						i++;
					}
				}
				if(encontrado){
					this->directorioActual=this->directorioActual->GetHijo(i);
				}else{
					cout<<"No encontrado el directorio"<<endl;
				}
			} else {
				cout << nomDir+": No es un directorio.\n";
			}
		}
	} else {
		cout << nomDir+": No existe.\n";
	}
}

void Arbol::mv(string nomDir,string nomNuevo){
	int i=0;
	bool encontrado=false;
	while(i<this->directorioActual->ChildNum() && !encontrado){
		encontrado=this->directorioActual->GetHijo(i)->GetNombre()==nomDir;
		if(!encontrado)
			i++;
	}
	if(encontrado)
		this->directorioActual->GetHijo(i)->SetNombre(nomNuevo);
	else
		cout<<"No encontrado el directorio"<<endl;
}

void Arbol::cp(string nomDir,string nomNuevo){
	if (this->directorioActual->GetNodoByNom(nomDir)->ChildNum() == 0) {
		bool encontrado=false;
		bool encontradoNuevo=false;
		int i=0,j=0;
		while(i<this->directorioActual->ChildNum() && !encontrado){
			encontrado=this->directorioActual->GetHijo(i)->GetNombre()==nomDir;
			if(!encontrado && !encontradoNuevo)
				i++;
		}
		while(j<this->directorioActual->ChildNum() && !encontradoNuevo){
			encontradoNuevo=this->directorioActual->GetHijo(j)->GetNombre()==nomNuevo;
			if(!encontradoNuevo)
				j++;
		}
		if(encontrado && encontradoNuevo){
			this->directorioActual->GetNodoByNom(nomNuevo)->AnadirHijo(this->directorioActual->GetNodoByNom(nomDir)->CopiarNodo());
			this->numeroNodos++;
			this->ultimoID++;
			this->directorioActual->GetNodoByNom(nomDir)->SetNumId(this->ultimoID);
		} else
			cout<<"No encontrado el directorio"<<endl;
	} else {
		cout << "Solo se puede copiar un directorio vacío.\n";
	}
}

void Arbol::mkdir(string nomDir){
	this->ultimoID++;
	this->numeroNodos++;
	Nodo* nuevo=new Nodo(nomDir,this->ultimoID,this->nivelActual,true);
	this->directorioActual->AnadirHijo(nuevo);
}

void Arbol::rmdir(string nomDir){
	if (this->directorioActual->GetNodoByNom(nomDir) != NULL) {
		if (this->directorioActual->GetNodoByNom(nomDir)->GetEsDir()) {
			this->directorioActual->EliminarNodo(this->directorioActual->GetNodoByNom(nomDir)->GetNumId());
			this->numeroNodos--;
		} else {
			cout << nomDir+": No es un directorio.\n";
		}
	} else {
		cout << nomDir+": No existe.\n";
	}
}

void Arbol::rm(string nomFile) {
	if (this->directorioActual->GetNodoByNom(nomFile) != NULL) {
		if (!this->directorioActual->GetNodoByNom(nomFile)->GetEsDir()) {
			this->ActualizarSectLib(this->directorioActual->GetNodoByNom(nomFile));
			this->directorioActual->EliminarNodo(this->directorioActual->GetNodoByNom(nomFile)->GetNumId());
			this->numeroNodos--;
			//this->posicionPuntero = 0;
		} else {
			cout << nomFile+": No es un fichero.\n";
		}
	} else {
		cout << nomFile+": No existe.\n";
	}
}

void Arbol::ActualizarSectLib(Nodo* n) {
	ofstream sectores1("secLibD1.dat",ios::in | ios::out);
	ofstream sectores2("secLibD2.dat",ios::in | ios::out);
	ofstream sectores3("secLibD3.dat",ios::in | ios::out);
	ofstream sectores4("secLibD4.dat",ios::in | ios::out);
	int r, i = 0, bloque = n->GetNumBloques()-1;
	const char l = 'L';
	while(bloque>=1){
		switch(bloque){
			case 1:{
				sectores1.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				for (r=0; r<1024; r++) {
					sectores1.write((char*)&l,1);
				}
				bloque=bloque-1;
				break;
			}
			case 2:{
				sectores1.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				sectores2.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				for (r=0; r<1024; r++) {
					sectores1.write((char*)&l,1);
					sectores2.write((char*)&l,1);
				}
				bloque=bloque-2;
				break;
			}
			case 3:{
				sectores1.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				sectores2.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				sectores3.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				for (r=0; r<1024; r++) {
					sectores1.write((char*)&l,1);
					sectores2.write((char*)&l,1);
					sectores3.write((char*)&l,1);
				}
				bloque=bloque-3;
				break;
			}
			case 4:{
				sectores1.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				sectores2.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				sectores3.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				sectores4.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				for (r=0; r<1024; r++) {
					sectores1.write((char*)&l,1);
					sectores2.write((char*)&l,1);
					sectores3.write((char*)&l,1);
					sectores4.write((char*)&l,1);
				}
				bloque=bloque-4;
				break;
			}
			default:{
				sectores1.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				sectores2.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				sectores3.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				sectores4.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
				i++;
				for (r=0; r<1024; r++) {
					sectores1.write((char*)&l,1);
					sectores2.write((char*)&l,1);
					sectores3.write((char*)&l,1);
					sectores4.write((char*)&l,1);
				}
				for (r=0; r<1024; r++) {
					sectores1.write((char*)&l,1);
				}
				bloque=bloque-5;
				break;
			}
		}
	}
	if(n->GetTamano()%1024>0){
		sectores1.seekp(n->GetBloqueIndiceByIndex(i)*1024,ios::beg);
		for (r=0; r<1024; r++) {
			sectores1.write((char*)&l,1);
		}
	}
	sectores1.close();
	sectores2.close();
	sectores3.close();
	sectores4.close();
}

void Arbol::upload(string nomFile){
	const char* nom = nomFile.c_str();
	ifstream f(nom);
	if (f != NULL) {
		off_t tamano = 0;
		char* lec = new char;
		this->ultimoID++;
		Nodo* nodo = new Nodo(nomFile, this->ultimoID, this->nivelActual, false);
		while (!f.eof()) {
			f.read((char*)lec,1);
			tamano++;
		}
		f.close();
		nodo->SetTamano(tamano);
		this->directorioActual->AnadirHijo(nodo);
		this->WriteFile(nomFile);
		if (tamano >= 1024) {
			this->posicionPuntero = this->posicionPuntero+(tamano/1024);
			if ((tamano % 1024) != 0) {
				this->posicionPuntero++;
			}
		} else {
			this->posicionPuntero++;
		}
	} else {
		cout << "El fichero "+nomFile+" no existe o no se encuentra en el directorio del programa.\n";
	}
}

void Arbol::SelectCommand(string command,string nomDir,string nomNuevo){
   map<string,int> mapeo;
   mapeo["ls"]=1;//lsm
   mapeo["pwd"]=2;//pwdm
   mapeo["cd"]=3;//cdm
   mapeo["mv"]=4;//mv
   mapeo["cp"]=5;//cp
   mapeo["mkdir"]=6;//mkdir
   mapeo["rmdir"]=7;//rmdir
   mapeo["rm"]=8;//rm
   mapeo["lls"]=9;//lls
   mapeo["lcd"]=10;//lcd
   mapeo["lpwd"]=11;//lpwd
   mapeo["upload"]=12;//upload
   switch(mapeo[command]){
	   case 1:{
		   ls();
		   break;
	   }
	   case 2:{
		   pwd();
		   break;
	   }
	   case 3:{
		   cd(nomDir);
		   break;
	   }
	   case 4:{
		   mv(nomDir,nomNuevo);
		   break;
	   }
	   case 5:{
		   cp(nomDir,nomNuevo);
		   break;
	   }
	   case 6:{
		   mkdir(nomDir);
		   break;
	   }
	   case 7:{
		   rmdir(nomDir);
		   break;
	   }
	   case 8:{
		   rm(nomDir);
		   break;
	   }
	   case 12:{
		   upload(nomDir);
		   break;
	   }
	   default: {
		   if (command != "exit")
			   cout << "El comando no se encuentra.\n";
		   break;
	   }
   }
}

void Arbol::WriteBlock(ofstream* disco,ofstream* sectores,ifstream* fichero) {
	int blqI;
	const char letra='O';
    int i;

	for(i=0;i<1024;i++){
		(*fichero).read((char*)&blqI,1);
		(*disco).write((char*)&blqI,1);
		(*sectores).write((char*)&letra,1);
	}
}


void Arbol::ReadBlock() {

}

void Arbol::WriteFile(string nomFile) {
	off_t sizeFichero;
	sizeFichero=this->directorioActual->GetNodoByNom(nomFile)->GetTamano();
	int bloque=sizeFichero/1024;
	int reserva = this->posicionPuntero/4;
	if (this->posicionPuntero >= 1) {
		if ((this->posicionPuntero%4) != 0) {
			reserva++;
		}
		reserva = reserva*1024;
	}

	ifstream fichero(nomFile.c_str(),ios::binary);

	ofstream disco1("disco1.dat",ios::in | ios::out);
	disco1.seekp(reserva,ios::beg);

	ofstream sectores1("secLibD1.dat",ios::in | ios::out);
	sectores1.seekp(reserva,ios::beg);

	ofstream disco2("disco2.dat",ios::in | ios::out);
	disco2.seekp(reserva,ios::beg);

	ofstream sectores2("secLibD2.dat",ios::in | ios::out);
	sectores2.seekp(reserva,ios::beg);

	ofstream disco3("disco3.dat",ios::in | ios::out);
	disco3.seekp(reserva,ios::beg);

	ofstream sectores3("secLibD3.dat",ios::in | ios::out);
	sectores3.seekp(reserva,ios::beg);

	ofstream disco4("disco4.dat",ios::in | ios::out);
	disco4.seekp(reserva,ios::beg);

	ofstream sectores4("secLibD4.dat",ios::binary | ios::app);
	sectores4.seekp(reserva,ios::beg);

	while(bloque>=1){
		switch(bloque){
			case 1:{
				this->WriteBlock(&disco1,&sectores1,&fichero);
				this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[0]);
				this->indDiscos[0]++;
				bloque=bloque-1;
				break;
			}
			case 2:{
				this->WriteBlock(&disco1,&sectores1,&fichero);
				this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[0]);
				this->indDiscos[0]++;
				this->WriteBlock(&disco2,&sectores2,&fichero);
				this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[1]);
				this->indDiscos[1]++;
				bloque=bloque-2;
				break;
			}
			case 3:{
				this->WriteBlock(&disco1,&sectores1,&fichero);
				this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[0]);
				this->indDiscos[0]++;
				this->WriteBlock(&disco2,&sectores2,&fichero);
				this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[1]);
				this->indDiscos[1]++;
				this->WriteBlock(&disco3,&sectores3,&fichero);
				this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[2]);
				this->indDiscos[2]++;
				bloque=bloque-3;
				break;
			}
			case 4:{
				this->WriteBlock(&disco1,&sectores1,&fichero);
				this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[0]);
				this->indDiscos[0]++;
				this->WriteBlock(&disco2,&sectores2,&fichero);
				this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[1]);
				this->indDiscos[1]++;
				this->WriteBlock(&disco3,&sectores3,&fichero);
				this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[2]);
				this->indDiscos[2]++;
				this->WriteBlock(&disco4,&sectores4,&fichero);
				this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[3]);
				this->indDiscos[3]++;
				bloque=bloque-4;
				break;
			}
			default:{
				this->WriteBlock(&disco1,&sectores1,&fichero);
				this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[0]);
				this->indDiscos[0]++;
				this->WriteBlock(&disco2,&sectores2,&fichero);
				this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[1]);
				this->indDiscos[1]++;
                this->WriteBlock(&disco3,&sectores3,&fichero);
                this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[2]);
                this->indDiscos[2]++;
                this->WriteBlock(&disco4,&sectores4,&fichero);
                this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[3]);
                this->indDiscos[3]++;
				this->WriteBlock(&disco1,&sectores1,&fichero);
				this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[0]);
				this->indDiscos[0]++;
				bloque=bloque-5;
				break;
			}
		}
	}
	if(sizeFichero%1024>0){
		this->WriteBlock(&disco1,&sectores1,&fichero);
		this->directorioActual->GetNodoByNom(nomFile)->AnadirBloqueIndice(this->indDiscos[0]);
		this->indDiscos[0]++;
	}

	disco1.close();
	sectores1.close();
	disco2.close();
	sectores2.close();
	disco3.close();
	sectores3.close();
	disco4.close();
	sectores4.close();
	fichero.close();
}

void Arbol::ReadFile() {

}
